<template>
  <nav>
    <div class="nav-container">
      <h1>{{ city }}</h1>
      <el-input
        type="text"
        v-model="inputCity"
        style="width: 240px"
        @keydown.enter="SearchCity"
        placeholder="请输入城市名称"
      >
        <template #prefix>
          <el-button :icon="Search" />
        </template>
      </el-input>
      <el-select
        v-model="brush"
        style="width: 240px"
        placeholder="选择绘制图形"
      >
        <el-option label="直线" value="line"></el-option>
        <el-option label="圆形" value="circle"></el-option>
        <el-option label="多边形" value="polygon"></el-option>
        <el-option label="自由画笔" value="free"></el-option>
        <el-option label="清除绘图" value="clear"></el-option>
      </el-select>
      <el-select
        ref="addIcon"
        v-model="iconType"
        style="width: 240px"
        placeholder="选择标记类型"
        :disabled="isCanClick"
        @click="successFromClick"
      >
        <el-option label="充电桩" value="charge"></el-option>
        <el-option label="加油站" value="gas"></el-option>
        <el-option label="公交站" value="bus"></el-option>
        <el-option label="退出标记" value="exit"></el-option>
      </el-select>
    </div>
  </nav>
  <div id="map">
    <div class="overlay"></div>
  </div>
</template>

<script setup>
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import VectorLayer from 'ol/layer/Vector.js';
import TileLayer from 'ol/layer/Tile.js';
import GeoJSON from 'ol/format/GeoJSON.js';
import { Fill, Style, Stroke } from 'ol/style.js';
import { XYZ } from 'ol/source';
import VectorSource from 'ol/source/Vector';
import { getThisCityByIp, getCityAdcode } from './api/index.js';
import { Search } from '@element-plus/icons-vue';
import Draw from 'ol/interaction/Draw.js';
import Overlay from 'ol/Overlay';
import Text from 'ol/style/Text';
import Cluster from 'ol/source/Cluster.js';
import CircleStyle from 'ol/style/Circle.js';
import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
import Icon from 'ol/style/Icon';
import { getLength, getArea } from 'ol/sphere';
import { ref, onMounted, watch } from 'vue';
import { ElMessage } from 'element-plus';

//当前城市
const city = ref('');
//画笔选项
const brush = ref('');
//添加标记选项
const iconType = ref('');
//标记选择框是否可点击，默认不可点击（true）
const isCanClick = ref(true);
//获取标记选择框元素实例
const addIcon = ref(null);
//输入框内容
const inputCity = ref('');

let map = new Map({});

let vectorLayer = new VectorLayer();

let view = new View({
  center: [116.46, 39.92],
  projection: 'EPSG:4326',
  zoom: 5,
  ZIndex: 0,
});

map.setView(view);

let vecLayer = new TileLayer({
  source: new XYZ({
    url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}',
  }),
});

//搜索城市
const SearchCity = () => {
  showCity(inputCity.value);
  inputCity.value = '';
};

//获取当前城市
const getThisCity = () => {
  getThisCityByIp().then((data) => {
    showCity(data.city);
  });
};

//显示城市区域
const showCity = (value) => {
  let center;
  let citySource = vectorLayer.getSource();
  if (citySource && citySource.clear) {
    citySource.clear();
  }
  getCityAdcode(value).then((data) => {
    if (data.status === '0') {
      alert('未找到该城市');
      return;
    }
    center = data.geocodes[0].location.split(',').map(Number);
    city.value = data.geocodes[0].city;
    let view = map.getView();
    view.animate({
      duration: 2000,
      center,
      zoom: 8,
    });
    const adcode = data.geocodes[0].adcode;
    vectorLayer = new VectorLayer({
      source: new VectorSource({
        url: `https://geo.datav.aliyun.com/areas_v3/bound/${adcode}.json`,
        format: new GeoJSON(),
        ZIndex: 1,
      }),
      style: new Style({
        fill: new Fill({
          color: 'rgba(255, 192, 203, 0.5)',
        }),
        stroke: new Stroke({
          color: 'lightblue',
          width: 2,
        }),
      }),
    });
    map.addLayer(vectorLayer);
  });
};

//map 还可以添加覆盖物(overlay) overlay本质上就是一个dom 但是这个Dom可以和ol进行关联
let overlay = new Overlay({
  positioning: 'top-center',
  offset: [0, 20],
});
map.on('click', function (e) {
  let lonlat = e.coordinate;
  let f = vectorLayer.getSource().getFeaturesAtCoordinate(lonlat)[0];
  if (f) {
    let name = f.get('name');
    let center = f.get('center');
    //获取到overlay对应的dom元素
    let element = overlay.getElement();
    element.innerHTML = `
      <div>
        <p>当前城市是：${name}</p>
        <p>经度：${center[0]}</p>
        <p>纬度：${center[1]}</p>
      </div>
    `;
    //设置overlay的坐标位置
    overlay.setPosition(center);
  } else {
    overlay.setPosition(undefined);
  }
});

//======================以下是绘图功能功的逻辑=====================
//定义一个矢量图层 用来放置draw绘制的要素
let drawLayer = new VectorLayer({
  source: new VectorSource(),
  style: new Style({
    stroke: new Stroke({
      color: 'red',
      width: 2,
    }),
    fill: new Fill({
      color: 'pink',
    }),
  }),
});
let draw = null;
const initDraw = () => {
  if (draw) {
    map.removeInteraction(draw);
  }
  if (brush.value === 'line') {
    draw = new Draw({
      type: 'LineString',
      source: drawLayer.getSource(),
    });
  } else if (brush.value === 'circle') {
    draw = new Draw({
      type: 'Circle',
      source: drawLayer.getSource(),
    });
  } else if (brush.value === 'polygon') {
    draw = new Draw({
      type: 'Polygon',
      source: drawLayer.getSource(),
    });
  } else if (brush.value === 'free') {
    draw = new Draw({
      type: 'LineString',
      source: drawLayer.getSource(),
      freehand: true,
    });
  } else if (brush.value === 'clear') {
    drawLayer.getSource().clear();
    draw = null;
    brush.value = '';
  }
  if (draw) {
    map.addInteraction(draw);
    draw.on('drawstart', (e) => {
      let f = e.feature;
      let g = f.getGeometry();
      g.on('change', (e) => {
        let g = e.target;
        //圆形面积计算计算还有问题，目前没有添加圆形面积计算
        if (brush.value === 'polygon') {
          let area = getArea(g, { projection: 'EPSG:4326' });
          area = area / 1000000;
          let c = g.getCoordinates();
          let position = c[c.length - 1][c.length - 1]; //这里为啥要套两层才获取到最后一个点的坐标
          let element = overlay.getElement();
          element.innerHTML = '当前的面积为：' + area.toFixed(2) + 'km²';
          overlay.setPosition(position);
        } else if (brush.value === 'line' || brush.value === 'free') {
          let length = getLength(g, { projection: 'EPSG:4326' });
          // length = length / 1000;
          let c = g.getCoordinates();
          let position = c[c.length - 1];
          let element = overlay.getElement();
          element.innerHTML = '当前的长度为：' + length.toFixed(2) + 'km';
          overlay.setPosition(position);
        } else {
          overlay.setPosition(undefined);
        }
      });
    });
  } else {
    overlay.setPosition(undefined);
  }
};
// 监听 brush.value 的变化
watch(brush, () => {
  initDraw();
});

//======================以下是标记选择框的逻辑======================
//提示框通知
const warningFrom = () => {
  ElMessage({
    message: '请将地图缩放至13级以上',
    type: 'warning',
  });
};
const successFromClick = () => {
  if (isCanClick.value) {
    warningFrom();
  }
};
let arr = [];

let layer = new VectorLayer({
  source: new VectorSource(),
  style: clusterStyle,
});

let mapClickListener = () => {};
const initIcon = () => {
  // 移除之前的监听器
  if (map.hasListener('click', mapClickListener)) {
    map.un('click', mapClickListener);
  }
  // 如果选择框值为 'exit' 或空，则清空 value2 并退出
  if (iconType.value === 'exit' || iconType.value === '') {
    iconType.value = '';
    return;
  }
  // 定义点击事件监听器
  mapClickListener = function (e) {
    const coordinate = e.coordinate;
    const point = new Feature({
      geometry: new Point(coordinate),
      type: iconType.value,
    });
    arr.push(point);
    // 更新矢量源
    let source = new VectorSource({
      features: arr,
    });
    let clusterSource = new Cluster({
      source: source,
      distance: 20, // 当点要素距离小于了10像素的时候 聚合在一起
    });
    layer.setSource(clusterSource);
  };
  // 添加点击事件监听器
  map.on('click', mapClickListener);
};

function clusterStyle(feature) {
  // 获取点要素是否聚合
  const features = feature.get('features');
  const size = features.length;
  let style = new Style();
  if (size === 1) {
    const type = features[0].get('type');
    if (type === 'charge') {
      style.setImage(
        new Icon({
          src: '../public/charge.png',
          scale: 0.2,
        })
      );
    } else if (type === 'gas') {
      style.setImage(
        new Icon({
          src: '../public/gas.png',
          scale: 0.2,
        })
      );
    } else if (type === 'bus') {
      style.setImage(
        new Icon({
          src: '../public/bus.png',
          scale: 0.3,
        })
      );
    } else {
      console.log('没有该类型:', type);
    }
  } else {
    //显示聚合的点的数量
    style.setImage(
      new CircleStyle({
        radius: 10,
        fill: new Fill({
          color: 'lightblue',
        }),
        stroke: new Stroke({
          color: '#fff',
          width: 2,
        }),
      })
    );
    style.setText(
      new Text({
        text: size.toString(),
        fill: new Fill({
          color: '#fff',
        }),
      })
    );
  }
  return style;
}

watch(iconType, () => {
  initIcon();
  layer.setStyle(clusterStyle);
});

//==========================以下是监听逻辑====================
onMounted(() => {
  //挂载完成后设置map的target
  map.setTarget('map');
  //添加地区边界矢量图层
  map.addLayer(vecLayer);
  //添加绘图矢量图层
  map.addLayer(drawLayer);
  //添加标记矢量图层
  map.addLayer(layer);
  //添加overlay
  overlay.setElement(document.querySelector('.overlay'));
  map.addOverlay(overlay);
  overlay.setVisible(true);
  //获取当前城市
  getThisCity();
});

//监听视图变化
view.on('change:resolution', () => {
  const newZoom = map.getView().getZoom();
  //判断是否显示矢量图层
  if (newZoom > 10) {
    vectorLayer.setVisible(false);
    overlay.setPosition(undefined);
    map.removeOverlay(overlay);
  } else {
    vectorLayer.setVisible(true);
    map.addOverlay(overlay);
  }
  //判断是否可以点击标记的选择框
  if (newZoom > 13) {
    isCanClick.value = false;
  } else {
    isCanClick.value = true;
    iconType.value = '';
  }
});
</script>

<style lang="scss">
// @import 'reset-css';
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
nav {
  display: flex;
  justify-items: center;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 999;
  width: 100%;
  height: 60px;
  background-color: lightblue;
  .nav-container {
    display: flex;
    justify-content: space-between;
    justify-items: center;
    padding: 0 80px;
    align-items: center;
    flex: 1;
    h1 {
      color: white;
      min-width: 80px;
      font-size: 28px;
      font-weight: bold;
    }
    .el-input {
      .el-input__wrapper {
        box-shadow: none;
        .el-button {
          border: none;
          padding: 8px 0;
        }
      }
    }
    .el-select {
      .el-select__wrapper {
        box-shadow: none;
      }
    }
  }
}
#map {
  height: 100vh;
  width: 100%;
  .overlay {
    background-color: white;
    padding: 15px;
    border-radius: 5px;
    box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.3);
    p {
      line-height: 26px;
    }
  }
  .overlay::after {
    width: 20px;
    height: 20px;
    content: '';
    position: absolute;
    top: -5px;
    left: 50%;
    transform: translateX(-50%) rotate(45deg);
    background-color: white;
  }
}
</style>
